package org.kiwi.job.service.impl;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.kiwi.job.component.RedisCacheManager;
import org.kiwi.job.core.ScheduleJob;
import org.kiwi.job.core.ScheduleJobManager;
import org.kiwi.job.orm.model.JobLogModel;
import org.kiwi.job.orm.model.JobModel;
import org.kiwi.job.orm.service.JobLogService;
import org.kiwi.job.orm.service.JobService;
import org.kiwi.job.service.ScheduleJobService;
import org.kiwi.job.vo.JobLogVO;
import org.kiwi.job.vo.JobVO;
import org.kiwi.util.mapping.BeanMapper;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.kiwi.job.constant.RedisConstant.QUARTZ_JOB_ALL;
import static org.kiwi.job.constant.RedisConstant.QUARTZ_JOB_DETAIL;
import static org.kiwi.job.constant.ScheduleConstant.REFRESH_JOB_CODE;

/**
 * @email jack.liu.19910921@gmail.com
 * Created by jack on 17/2/19.
 */
@Service
public class ScheduleJobServiceImpl implements ScheduleJobService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleJobServiceImpl.class);

    @Autowired
    private BeanMapper beanMapper;

    @Autowired
    private RedisCacheManager cacheManager;

    @Autowired
    private ScheduleJobManager scheduleJobManager;

    @Autowired
    private JobService jobService;

    @Autowired
    private JobLogService jobLogService;

    public void doRefresh() {
        List<JobModel> jobModelList = beanMapper.mapAsList(queryAllJob(), JobModel.class);
        if (CollectionUtils.isNotEmpty(jobModelList)) {
            for (JobModel jobModel : jobModelList) {
                try {
                    ScheduleJob job = beanMapper.map(jobModel, ScheduleJob.class);
                    //新建或重置任务状态
                    scheduleJobManager.doScheduleJob(job);
                } catch (Exception e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
    }

    @Override
    public void saveOrUpdateJob(JobVO job) {
        Assert.notNull(job, "job must not be null");

        JobModel jobModel = beanMapper.map(loadJob(job.getJobCode()), JobModel.class);
        if (jobModel != null) {
            JobModel upModel = new JobModel();
            if (StringUtils.isNotEmpty(job.getJobCron())) {
                if (!CronExpression.isValidExpression(job.getJobCron())) {
                    throw new IllegalArgumentException("cron表达式不正确");
                }
                upModel.setJobCron(job.getJobCron());
            }

            upModel.setJobName(job.getJobName());
            upModel.setJobDesc(job.getJobDesc());
            upModel.setIsConcurrent(job.getIsConcurrent());
            upModel.setJobStatus(job.getJobStatus());
            upModel.setJobClass(StringUtils.defaultIfBlank(job.getJobClass(), null));
            upModel.setJobMethod(StringUtils.defaultIfBlank(job.getJobMethod(), null));
            upModel.setHttpPath(StringUtils.defaultIfBlank(job.getHttpPath(), null));
            upModel.setJobPartitions(StringUtils.defaultIfBlank(job.getJobPartitions(), null));
            upModel.setJobScript(StringUtils.defaultIfBlank(job.getJobScript(), null));
            upModel.setUpdateTime(new Date());
            upModel.setId(jobModel.getId());
            jobService.updateByPrimaryKeySelective(upModel);
        } else {
            //验证job
            try {
                scheduleJobManager.validateJob(beanMapper.map(job, ScheduleJob.class));
            } catch (SchedulerException e) {
                throw new IllegalArgumentException(e);
            }
            //如果合法则保存到数据库
            jobModel = beanMapper.map(job, JobModel.class);
            jobModel.setCreateTime(new Date());
            jobModel.setUpdateTime(new Date());
            jobService.createSelective(jobModel);
        }
        loadJob(job.getJobCode(), true);
    }

    @Override
    public void saveJobLog(JobLogVO jobLog) {
        Assert.notNull(jobLog, "jobLog must not be null");

        if (REFRESH_JOB_CODE.equalsIgnoreCase(jobLog.getJobCode())) {
            return;
        }

        Timestamp now = new Timestamp(System.currentTimeMillis());
        JobModel old = jobService.findByJobCode(jobLog.getJobCode());
        //如果定时任务表中有此任务
        if (old != null) {
            JobModel jobModel = new JobModel();
            jobModel.setId(old.getId());
            jobModel.setPreviousTime(jobLog.getPreviousTime());
            jobModel.setNextTime(jobLog.getNextTime());
            jobModel.setCostTime(jobLog.getCostTime());
            jobModel.setUpdateTime(now);
            jobService.updateByPrimaryKeySelective(jobModel);

            JobLogModel logModel = beanMapper.map(jobLog, JobLogModel.class);
            logModel.setCreateTime(now);
            logModel.setUpdateTime(now);
            jobLogService.createSelective(logModel);
        }
    }

    @Override
    public List<JobLogVO> queryRunningRecordForPage(String jobCode, Long pageSize, Long pageNum) {
        List<JobLogModel> jobLogModelList = jobLogService.selectPageByJobCode(jobCode, pageNum, pageSize);
        return beanMapper.mapAsList(jobLogModelList, JobLogVO.class);
    }

    @Override
    public List<JobVO> queryAllJob() {
        try {
            List<JobModel> jobs = (List<JobModel>) cacheManager.get(QUARTZ_JOB_ALL);
            if (CollectionUtils.isNotEmpty(jobs)) {
                return beanMapper.mapAsList(jobs, JobVO.class);
            }

            List<JobModel> jobModelList = jobService.findAllJob();
            if (CollectionUtils.isNotEmpty(jobModelList)) {
                cacheManager.set(QUARTZ_JOB_ALL, jobModelList, 7, TimeUnit.DAYS);
                return beanMapper.mapAsList(jobModelList, JobVO.class);
            }

        } catch (Exception e) {
            LOGGER.error("queryAllJob()异常", e);
        }

        return null;
    }

    @Override
    public JobVO loadJob(String jobCode) {
        return loadJob(jobCode, false);
    }

    @Override
    public JobVO loadJob(String jobCode, boolean refresh) {
        try {
            String redisKey = QUARTZ_JOB_DETAIL + jobCode;

            JobModel jobModel = cacheManager.get(redisKey, JobModel.class);
            if (jobModel != null && !refresh) {
                return beanMapper.map(jobModel, JobVO.class);
            }

            jobModel = jobService.findByJobCode(jobCode);
            if (jobModel != null) {
                cacheManager.set(redisKey, jobModel, 7, TimeUnit.DAYS);
                cacheManager.delete(QUARTZ_JOB_ALL);
                return beanMapper.map(jobModel, JobVO.class);
            }

        } catch (Exception e) {
            LOGGER.error("加载JOB信息异常", e);

        }

        return null;
    }
}
